<!doctype html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=robots content="noodp"><meta http-equiv=x-ua-compatible content="IE=edge, chrome=1"><title>Spring Data JPA/Hibernate问题汇总 - Yulin Lewis' Blog</title><meta name=keywords content="雨临Lewis,Java,hugo,hexo,博客"><meta name=Description content="不想当写手的码农不是好咸鱼_(xз」∠)_"><meta property="og:title" content="Spring Data JPA/Hibernate问题汇总"><meta property="og:description" content="前言
本文基于如下版本的JPA和Hibernate：


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11


<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
  <version>2.4.0</version>
</dependency>

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>5.4.23.Final</version>
</dependency>"><meta property="og:type" content="article"><meta property="og:url" content="https://lewky.cn/posts/spring-data-jpa-hibernate/"><meta property="og:image" content="https://lewky.cn/logo.png"><meta property="article:published_time" content="2021-04-01T00:07:30+08:00"><meta property="article:modified_time" content="2022-03-01T00:07:30+08:00"><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="https://lewky.cn/logo.png"><meta name=twitter:title content="Spring Data JPA/Hibernate问题汇总"><meta name=twitter:description content="前言
本文基于如下版本的JPA和Hibernate：


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11


<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
  <version>2.4.0</version>
</dependency>

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>5.4.23.Final</version>
</dependency>"><meta name=application-name content="雨临Lewis的博客"><meta name=apple-mobile-web-app-title content="雨临Lewis的博客"><meta name=theme-color content="#ffffff"><meta name=msapplication-TileColor content="#da532c"><link rel="shortcut icon" type=image/x-icon href=/favicon.ico><link rel=icon type=image/png sizes=32x32 href=/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=/favicon-16x16.png><link rel=apple-touch-icon sizes=180x180 href=/apple-touch-icon.png><link rel=mask-icon href=/safari-pinned-tab.svg color=#5bbad5><link rel=manifest href=/site.webmanifest><link rel=canonical href=https://lewky.cn/posts/spring-data-jpa-hibernate/><link rel=prev href=https://lewky.cn/posts/docsify-0.html/><link rel=next href=https://lewky.cn/posts/plantuml-class-diagram.html/><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.min.css><link rel=stylesheet href=/css/style.min.css><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.13.0/css/all.min.css><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/animate.css@3.7.2/animate.min.css><script type=application/ld+json>{"@context":"http://schema.org","@type":"BlogPosting","headline":"Spring Data JPA/Hibernate问题汇总","inLanguage":"","mainEntityOfPage":{"@type":"WebPage","@id":"https:\/\/lewky.cn\/posts\/spring-data-jpa-hibernate\/"},"genre":"posts","keywords":"JPA, Hibernate, Spring, 工作记录","wordcount":7327,"url":"https:\/\/lewky.cn\/posts\/spring-data-jpa-hibernate\/","datePublished":"2021-04-01T00:07:30+08:00","dateModified":"2022-03-01T00:07:30+08:00","license":"This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.","publisher":{"@type":"Organization","name":"雨临Lewis","logo":"https:\/\/lewky.cn\/images\/avatar.jpg"},"author":{"@type":"Person","name":"雨临Lewis"},"description":""}</script></head><body header-desktop=fixed header-mobile=auto><script type=text/javascript>(window.localStorage&&localStorage.getItem('theme')?localStorage.getItem('theme')==='dark':('auto'==='auto'?window.matchMedia('(prefers-color-scheme: dark)').matches:'auto'==='dark'))&&document.body.setAttribute('theme','dark');</script><div id=mask></div><div class=wrapper><header class=desktop id=header-desktop><div class=header-wrapper><a href=https://github.com/lewky class=github-corner target=_blank title="Follow me on GitHub" aria-label="Follow me on GitHub"><svg width="3.5rem" height="3.5rem" viewBox="0 0 250 250" style="fill:#70b7fd;color:#fff;position:absolute;top:0;border:0;left:0;transform:scale(-1,1)" aria-hidden="true"><path d="M0 0 115 115h15l12 27L250 250V0z"/><path d="M128.3 109C113.8 99.7 119 89.6 119 89.6 122 82.7 120.5 78.6 120.5 78.6 119.2 72 123.4 76.3 123.4 76.3 127.3 80.9 125.5 87.3 125.5 87.3 122.9 97.6 130.6 101.9 134.4 103.2" fill="currentcolor" style="transform-origin:130px 106px" class="octo-arm"/><path d="M115 115C114.9 115.1 118.7 116.5 119.8 115.4L133.7 101.6C136.9 99.2 139.9 98.4 142.2 98.6 133.8 88 127.5 74.4 143.8 58 148.5 53.4 154 51.2 159.7 51 160.3 49.4 163.2 43.6 171.4 40.1 171.4 40.1 176.1 42.5 178.8 56.2 183.1 58.6 187.2 61.8 190.9 65.4 194.5 69 197.7 73.2 200.1 77.6 213.8 80.2 216.3 84.9 216.3 84.9 212.7 93.1 206.9 96 205.4 96.6 205.1 102.4 203 107.8 198.3 112.5 181.9 128.9 168.3 122.5 157.7 114.1 157.9 116.9 156.7 120.9 152.7 124.9L141 136.5C139.8 137.7 141.6 141.9 141.8 141.8z" fill="currentcolor" class="octo-body"/></svg></a><div class=header-title><a href=/ title="Yulin Lewis' Blog"><span class=header-title-pre><i class="fas fa-fw fa-atom"></i></span>雨临Lewis</a></div><div class=menu><div class=menu-inner><div class=dropdown><a href=/posts/ class="menu-item menu-more dropbtn" title=点击查看所有文章><i class="fas fa-fw fa-archive"></i>归档</a><div class="menu-more-content dropdown-content"><a href=/categories/><i class="fas fa-fw fa-th"></i>分类 </a><a href=/tags/><i class="fas fa-fw fa-tag"></i>标签 </a><a href=/hot/ title=文章热度Top15><i class="fas fa-fw fa-fire"></i>热度 </a><a href=/donation/ title=感谢打赏，老板大气~><i class="fas fa-fw fa-donate"></i>打赏 </a><a href=/about/><i class="fas fa-fw fa-at"></i>关于</a></div></div><a class=menu-item href=/friends/ title=欢迎申请友链><i class="fas fa-fw fa-link"></i>友链</a><div class=dropdown><a href=javascript:void(0); class="menu-item menu-more dropbtn" title=一些有意思的东东~><i class="fas fa-fw fa-fan fa-spin"></i>趣味</a><div class="menu-more-content dropdown-content"><a href=/funny/high/ title="前方高能♂ 小心外放！" target=_blank rel=noopener><i class="fas fa-fw fa-dizzy"></i>燥起来！ </a><a href=/funny/mikutap/ title=初音未来音乐游戏 target=_blank rel=noopener><i class="fas fa-fw fa-music"></i>在线打碟 </a><a href=/funny/catch-the-cat/ title=逮住那只猫!><i class="fas fa-fw fa-cat"></i>抓住猫咪</a></div></div><div class=dropdown><a href=/ class="menu-item menu-more dropbtn" title=回到首页><i class="fas fa-fw fa-atom"></i>站点</a><div class="menu-more-content dropdown-content"><a href=/bbs/ title=来留言吧~><i class="fas fa-fw fa-comment"></i>公告留言 </a><a href=https://seo.chinaz.com target=_blank rel=noopener><i class="fas fa-fw fa-chart-line"></i>SEO查询 </a><a href=https://www.ping.cn/http/lewky.cn target=_blank rel=noopener><i class="fas fa-fw fa-bezier-curve"></i>网站测速 </a><a href=/posts/e62c38c4.html/><i class="fas fa-fw fa-cog fa-spin"></i>建站日志</a></div></div><div class=dropdown><a href=javascript:void(0); class="menu-item menu-more dropbtn"><i class="fas fa-fw fa-book"></i>文档</a><div class="menu-more-content dropdown-content"><a href=https://gohugo.io/documentation/ target=_blank rel=noopener><i class="fas fa-fw fa-star"></i>Hugo文档 </a><a href=https://javanote.doc.lewky.cn/ title=尚在完善中~ target=_blank rel=noopener><i class="fab fa-fw fa-java"></i>Java 笔记</a></div></div><a class=menu-item href=/search/ title=渲染搜索结果需要等待几秒钟~><i class="fas fa-fw fa-search"></i>搜索
</a><span class="menu-item delimiter"></span><a href=https://travellings.link target=_blank class=menu-item rel=noopener title=开往-友链接力><i class="fas fa-fw fa-subway"></i></a><a href=https://rssblog.vercel.app/ class=menu-item target=_blank rel=noopener title=RSSBlog><i class="fas fa-fw fa-inbox"></i></a><a href=https://github.com/lewky class=menu-item target=_blank rel=noopener title=GitHub><i class="fab fa-fw fa-github"></i></a></div></div></div></header><header class=mobile id=header-mobile><div class=header-container><div class=header-wrapper><div class=header-title><a href=/ title="Yulin Lewis' Blog"><span class=header-title-pre><i class="fas fa-fw fa-atom"></i></span>雨临Lewis</a></div><div class=menu-toggle id=menu-toggle-mobile><span></span><span></span><span></span></div></div><div class=menu id=menu-mobile><div class=dropdown><a href=/posts/ class="menu-item menu-more dropbtn" title=点击查看所有文章><i class="fas fa-fw fa-archive"></i>归档</a><div class="menu-more-content dropdown-content"><a href=/categories/><i class="fas fa-fw fa-th"></i>分类 </a><a href=/tags/><i class="fas fa-fw fa-tag"></i>标签 </a><a href=/hot/ title=文章热度Top15><i class="fas fa-fw fa-fire"></i>热度 </a><a href=/donation/ title=感谢打赏，老板大气~><i class="fas fa-fw fa-donate"></i>打赏 </a><a href=/about/><i class="fas fa-fw fa-at"></i>关于</a></div></div><a class=menu-item href=/friends/ title=欢迎申请友链><i class="fas fa-fw fa-link"></i>友链</a><div class=dropdown><a href=javascript:void(0); class="menu-item menu-more dropbtn" title=一些有意思的东东~><i class="fas fa-fw fa-fan fa-spin"></i>趣味</a><div class="menu-more-content dropdown-content"><a href=/funny/high/ title="前方高能♂ 小心外放！" target=_blank rel=noopener><i class="fas fa-fw fa-dizzy"></i>燥起来！ </a><a href=/funny/mikutap/ title=初音未来音乐游戏 target=_blank rel=noopener><i class="fas fa-fw fa-music"></i>在线打碟 </a><a href=/funny/catch-the-cat/ title=逮住那只猫!><i class="fas fa-fw fa-cat"></i>抓住猫咪</a></div></div><div class=dropdown><a href=/ class="menu-item menu-more dropbtn" title=回到首页><i class="fas fa-fw fa-atom"></i>站点</a><div class="menu-more-content dropdown-content"><a href=/bbs/ title=来留言吧~><i class="fas fa-fw fa-comment"></i>公告留言 </a><a href=https://seo.chinaz.com target=_blank rel=noopener><i class="fas fa-fw fa-chart-line"></i>SEO查询 </a><a href=https://www.ping.cn/http/lewky.cn target=_blank rel=noopener><i class="fas fa-fw fa-bezier-curve"></i>网站测速 </a><a href=/posts/e62c38c4.html/><i class="fas fa-fw fa-cog fa-spin"></i>建站日志</a></div></div><div class=dropdown><a href=javascript:void(0); class="menu-item menu-more dropbtn"><i class="fas fa-fw fa-book"></i>文档</a><div class="menu-more-content dropdown-content"><a href=https://gohugo.io/documentation/ target=_blank rel=noopener><i class="fas fa-fw fa-star"></i>Hugo文档 </a><a href=https://javanote.doc.lewky.cn/ title=尚在完善中~ target=_blank rel=noopener><i class="fab fa-fw fa-java"></i>Java 笔记</a></div></div><a class=menu-item href=/search/ title=渲染搜索结果需要等待几秒钟~><i class="fas fa-fw fa-search"></i>搜索</a>
<a href=https://travellings.link target=_blank class=menu-item rel=noopener title=开往-友链接力><i class="fas fa-fw fa-subway"></i></a><a href=https://rssblog.vercel.app/ class=menu-item target=_blank rel=noopener title=RSSBlog><i class="fas fa-fw fa-inbox"></i></a><a href=https://github.com/lewky class=menu-item target=_blank rel=noopener title=GitHub><i class="fab fa-fw fa-github"></i></a></div></div></header><div class="search-dropdown desktop"><div id=search-dropdown-desktop></div></div><div class="search-dropdown mobile"><div id=search-dropdown-mobile></div></div><main class=main><div class=container><div class=toc id=toc-auto><h2 class=toc-title>目录</h2><div class=toc-content id=toc-content-auto></div></div><article class="page single"><h1 class="single-title animated flipInX">Spring Data JPA/Hibernate问题汇总</h1><div class=post-meta><div class=post-meta-line><span class=post-author><a href=/ title=Author rel=author class=author><i class="fas fa-user-circle fa-fw"></i>雨临Lewis</a></span>&nbsp;<span class=post-category>收录于 <a href=/categories/%E5%B7%A5%E4%BD%9C%E8%AE%B0%E5%BD%95/><i class="far fa-folder fa-fw"></i>工作记录</a></span></div><div class=post-meta-line><i class="far fa-calendar fa-fw"></i>&nbsp;<time datetime=2021-04-01>2021-04-01</time>&nbsp;<i class="far fa-calendar-plus fa-fw"></i>&nbsp;<time datetime=2022-03-01>2022-03-01</time>&nbsp;<i class="fas fa-pencil-alt fa-fw"></i>&nbsp;约 7327 字
<i class="far fa-clock fa-fw"></i>&nbsp;预计阅读 15 分钟&nbsp;<span id=/posts/spring-data-jpa-hibernate/ class=leancloud_visitors data-flag-title="Spring Data JPA/Hibernate问题汇总">
<i class="far fa-eye fa-fw"></i>&nbsp;<span class=leancloud-visitors-count></span>&nbsp;次阅读
</span>&nbsp;
<a href=#comments id=post-meta-vcount title=查看评论><i class="fas fa-comment fa-fw"></i>&nbsp;<span id=/posts/spring-data-jpa-hibernate/ class=waline-comment-count></span>&nbsp;条评论</a></div></div><div class="details toc" id=toc-static kept><div class="details-summary toc-title"><span>目录</span>
<span><i class="details-icon fas fa-angle-right"></i></span></div><div class="details-content toc-content" id=toc-content-static><nav id=TableOfContents><ol><li><a href=#前言>前言</a></li><li><a href=#懒加载异常---jsonmappingexception-could-not-initialize-proxy>懒加载异常 - JsonMappingException: could not initialize proxy</a><ol><li><a href=#解决方法一>解决方法一</a></li><li><a href=#解决方法二>解决方法二</a></li><li><a href=#懒加载源码分析>懒加载源码分析</a></li></ol></li><li><a href=#joincolumn无法找回数据导致报错>@JoinColumn无法找回数据导致报错</a></li><li><a href=#连表时指定额外的条件>连表时指定额外的条件</a></li><li><a href=#懒加载导致的n--1问题>懒加载导致的N + 1问题</a></li><li><a href=#cannot-simultaneously-fetch-multiple-bags异常>cannot simultaneously fetch multiple bags异常</a></li><li><a href=#unexpectedrollbackexception异常>UnexpectedRollbackException异常</a></li><li><a href=#jpa-projection不支持新的日期类localdatelocaldatetime>JPA Projection不支持新的日期类LocalDate、LocalDateTime</a></li><li><a href=#operator-does-not-exist-character-varying--bytea>operator does not exist: character varying = bytea</a></li><li><a href=#忽略某个字段>忽略某个字段</a></li><li><a href=#springboot打印hibernate的sql>SpringBoot打印Hibernate的sql</a></li><li><a href=#detached-entity-passed-to-persist>detached entity passed to persist</a></li><li><a href=#id生成策略>id生成策略</a><ol><li><a href=#sequence策略>sequence策略</a></li><li><a href=#identity策略>identity策略</a></li><li><a href=#其他的写法>其他的写法</a></li></ol></li><li><a href=#nativequery>nativeQuery</a></li><li><a href=#参考链接>参考链接</a></li></ol></nav></div></div><div class=content id=content><h2 id=前言>前言</h2><p>本文基于如下版本的JPA和Hibernate：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-xml data-lang=xml><span class=nt>&lt;dependency&gt;</span>
  <span class=nt>&lt;groupId&gt;</span>org.springframework.boot<span class=nt>&lt;/groupId&gt;</span>
  <span class=nt>&lt;artifactId&gt;</span>spring-boot-starter-data-jpa<span class=nt>&lt;/artifactId&gt;</span>
  <span class=nt>&lt;version&gt;</span>2.4.0<span class=nt>&lt;/version&gt;</span>
<span class=nt>&lt;/dependency&gt;</span>

<span class=nt>&lt;dependency&gt;</span>
  <span class=nt>&lt;groupId&gt;</span>org.hibernate<span class=nt>&lt;/groupId&gt;</span>
  <span class=nt>&lt;artifactId&gt;</span>hibernate-core<span class=nt>&lt;/artifactId&gt;</span>
  <span class=nt>&lt;version&gt;</span>5.4.23.Final<span class=nt>&lt;/version&gt;</span>
<span class=nt>&lt;/dependency&gt;</span>
</code></pre></td></tr></table></div></div><h2 id=懒加载异常---jsonmappingexception-could-not-initialize-proxy>懒加载异常 - JsonMappingException: could not initialize proxy</h2><p>查询数据时报懒加载异常：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=n>Caused</span> <span class=n>by</span><span class=o>:</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>LazyInitializationException</span><span class=o>:</span> <span class=n>could</span> <span class=n>not</span> <span class=n>initialize</span> <span class=n>proxy</span> <span class=o>[</span><span class=n>com</span><span class=o>.</span><span class=na>cbxsoftware</span><span class=o>.</span><span class=na>cbx</span><span class=o>.</span><span class=na>attachment</span><span class=o>.</span><span class=na>entity</span><span class=o>.</span><span class=na>RefAttachment</span><span class=err>#</span><span class=n>c109ec36e60c4a89a10eabc72416d984</span><span class=o>]</span> <span class=o>-</span> <span class=n>no</span> <span class=n>Session</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>AbstractLazyInitializer</span><span class=o>.</span><span class=na>initialize</span><span class=o>(</span><span class=n>AbstractLazyInitializer</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>169</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>AbstractLazyInitializer</span><span class=o>.</span><span class=na>getImplementation</span><span class=o>(</span><span class=n>AbstractLazyInitializer</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>309</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>pojo</span><span class=o>.</span><span class=na>bytebuddy</span><span class=o>.</span><span class=na>ByteBuddyInterceptor</span><span class=o>.</span><span class=na>intercept</span><span class=o>(</span><span class=n>ByteBuddyInterceptor</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>45</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>ProxyConfiguration$InterceptorDispatcher</span><span class=o>.</span><span class=na>intercept</span><span class=o>(</span><span class=n>ProxyConfiguration</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>95</span><span class=o>)</span>
</code></pre></td></tr></table></div></div><p>报错很明显，是由于hibernate的懒加载引起的。项目使用的是SpringBoot框架，JPA默认使用的是hibernate的实现，而hibernate的懒加载机制其实就是延迟加载对象，如果没有在session关闭前使用到对象里除id以外的属性时，就只会返回一个没有初始化过的包含了id的代理类。很多时候，这个代理类会引发上述的异常。</p><p>简单说一下为什么会触发懒加载异常，首先hibernate开启一个session（会话），然后开启transaction（事务），接着发出sql找回数据并组装成pojo（或者说entity、model），这时候如果pojo里有懒加载的对象，并不会去发出sql查询db，而是直接返回一个懒加载的代理对象，这个对象里只有id。</p><p>如果接下来没有其他的操作去访问这个代理对象除了id以外的属性，就不会去初始化这个代理对象，也就不会去发出sql查找db。接着事务提交，session关闭。如果这时候再去访问代理对象除了id以外的属性时，就会报上述的懒加载异常，原因是这时候已经没有session了，无法初始化懒加载的代理对象。</p><h3 id=解决方法一>解决方法一</h3><p>如果是spring集成的hibernate，根据上述的原因，可以延长session的生命周期，但是这里用的是SpringBoot的JPA，处理方法不同，需要在<code>application.properties</code>配置下懒加载相关的东西：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-xml data-lang=xml>spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
</code></pre></td></tr></table></div></div><p>进行该配置后，可以在session关闭时也能另外开启一个新的session和事务来访问db以取回懒加载对象的数据。</p><h3 id=解决方法二>解决方法二</h3><p>因为该懒加载异常是缺少session导致的，那么可以通过在方法前添加事务注解<code>@Transactional</code>的方式来解决，只要事务没有提交，session就不会关闭，自然就不会出现上述的懒加载异常。不过由于该事务注解是用Spring AOP实现的，存在着一些坑，比如类内直接调用无效或者对非public方法无效等，需要多加注意。</p><p>当使用了上述两种方法后，发现不再触发<code>LazyInitializationException</code>，但是却发生了另一个新的异常<code>InvalidDefinitionException</code>：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span><span class=lnt>6
</span><span class=lnt>7
</span><span class=lnt>8
</span><span class=lnt>9
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=n>com</span><span class=o>.</span><span class=na>fasterxml</span><span class=o>.</span><span class=na>jackson</span><span class=o>.</span><span class=na>databind</span><span class=o>.</span><span class=na>exc</span><span class=o>.</span><span class=na>InvalidDefinitionException</span><span class=o>:</span> <span class=n>No</span> <span class=n>serializer</span> <span class=n>found</span> <span class=k>for</span> <span class=kd>class</span> <span class=nc>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>pojo</span><span class=o>.</span><span class=na>bytebuddy</span><span class=o>.</span><span class=na>ByteBuddyInterceptor</span> <span class=n>and</span> <span class=n>no</span> <span class=n>properties</span> <span class=n>discovered</span> <span class=n>to</span> <span class=n>create</span> <span class=nf>BeanSerializer</span> <span class=o>(</span><span class=n>to</span> <span class=n>avoid</span> <span class=n>exception</span><span class=o>,</span> <span class=n>disable</span> <span class=n>SerializationFeature</span><span class=o>.</span><span class=na>FAIL_ON_EMPTY_BEANS</span><span class=o>)</span> <span class=o>(</span><span class=n>through</span> <span class=n>reference</span> <span class=n>chain</span><span class=o>:</span> <span class=n>com</span><span class=o>.</span><span class=na>cbxsoftware</span><span class=o>.</span><span class=na>cbx</span><span class=o>.</span><span class=na>item</span><span class=o>.</span><span class=na>elasticsearch</span><span class=o>.</span><span class=na>entity</span><span class=o>.</span><span class=na>ItemEstc</span><span class=o>[</span><span class=s>&#34;mainEntity&#34;</span><span class=o>]-&gt;</span><span class=n>com</span><span class=o>.</span><span class=na>cbxsoftware</span><span class=o>.</span><span class=na>cbx</span><span class=o>.</span><span class=na>item</span><span class=o>.</span><span class=na>entity</span><span class=o>.</span><span class=na>Item</span><span class=o>[</span><span class=s>&#34;image&#34;</span><span class=o>]-&gt;</span><span class=n>com</span><span class=o>.</span><span class=na>cbxsoftware</span><span class=o>.</span><span class=na>cbx</span><span class=o>.</span><span class=na>image</span><span class=o>.</span><span class=na>entity</span><span class=o>.</span><span class=na>RefImage</span><span class=o>[</span><span class=s>&#34;propFormat&#34;</span><span class=o>]-&gt;</span><span class=n>com</span><span class=o>.</span><span class=na>cbxsoftware</span><span class=o>.</span><span class=na>cbx</span><span class=o>.</span><span class=na>attachment</span><span class=o>.</span><span class=na>entity</span><span class=o>.</span><span class=na>RefAttachment$HibernateProxy$vTKSYzrN</span><span class=o>[</span><span class=s>&#34;hibernateLazyInitializer&#34;</span><span class=o>])</span>
	<span class=n>at</span> <span class=n>com</span><span class=o>.</span><span class=na>fasterxml</span><span class=o>.</span><span class=na>jackson</span><span class=o>.</span><span class=na>databind</span><span class=o>.</span><span class=na>exc</span><span class=o>.</span><span class=na>InvalidDefinitionException</span><span class=o>.</span><span class=na>from</span><span class=o>(</span><span class=n>InvalidDefinitionException</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>77</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>com</span><span class=o>.</span><span class=na>fasterxml</span><span class=o>.</span><span class=na>jackson</span><span class=o>.</span><span class=na>databind</span><span class=o>.</span><span class=na>SerializerProvider</span><span class=o>.</span><span class=na>reportBadDefinition</span><span class=o>(</span><span class=n>SerializerProvider</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>1191</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>com</span><span class=o>.</span><span class=na>fasterxml</span><span class=o>.</span><span class=na>jackson</span><span class=o>.</span><span class=na>databind</span><span class=o>.</span><span class=na>DatabindContext</span><span class=o>.</span><span class=na>reportBadDefinition</span><span class=o>(</span><span class=n>DatabindContext</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>313</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>com</span><span class=o>.</span><span class=na>fasterxml</span><span class=o>.</span><span class=na>jackson</span><span class=o>.</span><span class=na>databind</span><span class=o>.</span><span class=na>ser</span><span class=o>.</span><span class=na>impl</span><span class=o>.</span><span class=na>UnknownSerializer</span><span class=o>.</span><span class=na>failForEmpty</span><span class=o>(</span><span class=n>UnknownSerializer</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>71</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>com</span><span class=o>.</span><span class=na>fasterxml</span><span class=o>.</span><span class=na>jackson</span><span class=o>.</span><span class=na>databind</span><span class=o>.</span><span class=na>ser</span><span class=o>.</span><span class=na>impl</span><span class=o>.</span><span class=na>UnknownSerializer</span><span class=o>.</span><span class=na>serialize</span><span class=o>(</span><span class=n>UnknownSerializer</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>33</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>com</span><span class=o>.</span><span class=na>fasterxml</span><span class=o>.</span><span class=na>jackson</span><span class=o>.</span><span class=na>databind</span><span class=o>.</span><span class=na>ser</span><span class=o>.</span><span class=na>BeanPropertyWriter</span><span class=o>.</span><span class=na>serializeAsField</span><span class=o>(</span><span class=n>BeanPropertyWriter</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>727</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>com</span><span class=o>.</span><span class=na>fasterxml</span><span class=o>.</span><span class=na>jackson</span><span class=o>.</span><span class=na>databind</span><span class=o>.</span><span class=na>ser</span><span class=o>.</span><span class=na>std</span><span class=o>.</span><span class=na>BeanSerializerBase</span><span class=o>.</span><span class=na>serializeFields</span><span class=o>(</span><span class=n>BeanSerializerBase</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>719</span><span class=o>)</span>
	<span class=o>...</span>
</code></pre></td></tr></table></div></div><p>这个异常是由于hibernate在代理类里添加了一个属性<code>hibernateLazyInitializer</code>，当该对象转换成json的时候就会报错。解决方法是将该属性过滤掉，可以在对应的类名或者公共类前加上如下注解：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=nd>@JsonIgnoreProperties</span><span class=o>(</span><span class=n>value</span> <span class=o>=</span> <span class=o>{</span> <span class=s>&#34;hibernateLazyInitializer&#34;</span> <span class=o>})</span>
</code></pre></td></tr></table></div></div><h3 id=懒加载源码分析>懒加载源码分析</h3><p>因为对懒加载异常的发生有些好奇，所以看了下hibernate的源码，这里简单分析下，另外我看的是两个源码包如下：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-fallback data-lang=fallback>spring-orm-5.1.5.RELEASE.jar
hibernate-core-5.3.7.Final.jar
</code></pre></td></tr></table></div></div><p>首先是关于<code>spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true</code>的配置，前面半截是因为JPA集成了hibernate的配置，所以在hibernate中，这个配置应该是<code>hibernate.enable_lazy_load_no_trans=true</code>。</p><p>在hibernate的一个常量接口<code>org.hibernate.cfg.AvailableSettings</code>中定义了各种配置常量，其中就包括上述这个配置：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=n>String</span> <span class=n>ENABLE_LAZY_LOAD_NO_TRANS</span> <span class=o>=</span> <span class=s>&#34;hibernate.enable_lazy_load_no_trans&#34;</span><span class=o>;</span>
</code></pre></td></tr></table></div></div><p>在启动项目的时候会读取配置文件，将其解析为一个<code>HashMap&lt;K,V></code>，这些参数在new<code>EntityManagerFactoryBuilderImpl</code>的时候被使用到，上面的常量会在<code>org.hibernate.boot.internal.SessionFactoryOptionsBuilder</code>里被拿来初始化：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=k>this</span><span class=o>.</span><span class=na>initializeLazyStateOutsideTransactions</span> <span class=o>=</span> <span class=n>cfgService</span><span class=o>.</span><span class=na>getSetting</span><span class=o>(</span> <span class=n>ENABLE_LAZY_LOAD_NO_TRANS</span><span class=o>,</span> <span class=n>BOOLEAN</span><span class=o>,</span> <span class=kc>false</span> <span class=o>);</span>
</code></pre></td></tr></table></div></div><p>因为在配置文件里配置了该变量的值为true，所以这里在初始化的时候就会把<code>initializeLazyStateOutsideTransactions</code>的值设置为true。该变量由一个方法来判断其值是否为true：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@Override</span>
	<span class=kd>public</span> <span class=kt>boolean</span> <span class=nf>isInitializeLazyStateOutsideTransactionsEnabled</span><span class=o>()</span> <span class=o>{</span>
		<span class=k>return</span> <span class=n>initializeLazyStateOutsideTransactions</span><span class=o>;</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>接着在组装pojo时，会为懒加载对象创建对应的代理对象，当需要获取该代理对象除id以外的属性时，就会调用<code>AbstractLazyInitializer#initialize()</code>进行初始化，逻辑如下：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span><span class=lnt>21
</span><span class=lnt>22
</span><span class=lnt>23
</span><span class=lnt>24
</span><span class=lnt>25
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java>	<span class=nd>@Override</span>
	<span class=kd>public</span> <span class=kd>final</span> <span class=kt>void</span> <span class=nf>initialize</span><span class=o>()</span> <span class=kd>throws</span> <span class=n>HibernateException</span> <span class=o>{</span>
		<span class=k>if</span> <span class=o>(</span> <span class=o>!</span><span class=n>initialized</span> <span class=o>)</span> <span class=o>{</span>
			<span class=k>if</span> <span class=o>(</span> <span class=n>allowLoadOutsideTransaction</span> <span class=o>)</span> <span class=o>{</span>
				<span class=n>permissiveInitialization</span><span class=o>();</span>
			<span class=o>}</span>
			<span class=k>else</span> <span class=k>if</span> <span class=o>(</span> <span class=n>session</span> <span class=o>==</span> <span class=kc>null</span> <span class=o>)</span> <span class=o>{</span>
				<span class=k>throw</span> <span class=k>new</span> <span class=n>LazyInitializationException</span><span class=o>(</span> <span class=s>&#34;could not initialize proxy [&#34;</span> <span class=o>+</span> <span class=n>entityName</span> <span class=o>+</span> <span class=s>&#34;#&#34;</span> <span class=o>+</span> <span class=n>id</span> <span class=o>+</span> <span class=s>&#34;] - no Session&#34;</span> <span class=o>);</span>
			<span class=o>}</span>
			<span class=k>else</span> <span class=k>if</span> <span class=o>(</span> <span class=o>!</span><span class=n>session</span><span class=o>.</span><span class=na>isOpen</span><span class=o>()</span> <span class=o>)</span> <span class=o>{</span>
				<span class=k>throw</span> <span class=k>new</span> <span class=n>LazyInitializationException</span><span class=o>(</span> <span class=s>&#34;could not initialize proxy [&#34;</span> <span class=o>+</span> <span class=n>entityName</span> <span class=o>+</span> <span class=s>&#34;#&#34;</span> <span class=o>+</span> <span class=n>id</span> <span class=o>+</span> <span class=s>&#34;] - the owning Session was closed&#34;</span> <span class=o>);</span>
			<span class=o>}</span>
			<span class=k>else</span> <span class=k>if</span> <span class=o>(</span> <span class=o>!</span><span class=n>session</span><span class=o>.</span><span class=na>isConnected</span><span class=o>()</span> <span class=o>)</span> <span class=o>{</span>
				<span class=k>throw</span> <span class=k>new</span> <span class=n>LazyInitializationException</span><span class=o>(</span> <span class=s>&#34;could not initialize proxy [&#34;</span> <span class=o>+</span> <span class=n>entityName</span> <span class=o>+</span> <span class=s>&#34;#&#34;</span> <span class=o>+</span> <span class=n>id</span> <span class=o>+</span> <span class=s>&#34;] - the owning Session is disconnected&#34;</span> <span class=o>);</span>
			<span class=o>}</span>
			<span class=k>else</span> <span class=o>{</span>
				<span class=n>target</span> <span class=o>=</span> <span class=n>session</span><span class=o>.</span><span class=na>immediateLoad</span><span class=o>(</span> <span class=n>entityName</span><span class=o>,</span> <span class=n>id</span> <span class=o>);</span>
				<span class=n>initialized</span> <span class=o>=</span> <span class=kc>true</span><span class=o>;</span>
				<span class=n>checkTargetState</span><span class=o>(</span><span class=n>session</span><span class=o>);</span>
			<span class=o>}</span>
		<span class=o>}</span>
		<span class=k>else</span> <span class=o>{</span>
			<span class=n>checkTargetState</span><span class=o>(</span><span class=n>session</span><span class=o>);</span>
		<span class=o>}</span>
	<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>如果在配置文件中设置了<code>spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true</code>，那么上述的<code>allowLoadOutsideTransaction</code>变量值就为true，则可以进入<code>permissiveInitialization()</code>方法另起session和事务，最终避免懒加载异常<code>LazyInitializationException</code>。如果没有配置该参数，那么就会由于session已关闭（即为null）而抛出<code>LazyInitializationException</code>。</p><h2 id=joincolumn无法找回数据导致报错>@JoinColumn无法找回数据导致报错</h2><p>使用<code>@JoinColumn</code>时如果无法找到对应的record，就会报错导致查询失败：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span><span class=lnt>6
</span><span class=lnt>7
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=n>javax</span><span class=o>.</span><span class=na>persistence</span><span class=o>.</span><span class=na>EntityNotFoundException</span><span class=o>:</span> <span class=n>Unable</span> <span class=n>to</span> <span class=n>find</span> <span class=n>com</span><span class=o>.</span><span class=na>cbxsoftware</span><span class=o>.</span><span class=na>rest</span><span class=o>.</span><span class=na>entity</span><span class=o>.</span><span class=na>fact</span><span class=o>.</span><span class=na>Fact</span> <span class=n>with</span> <span class=n>id</span> <span class=n>4d644cfa243b493ab34d69e4207ee5f1</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>jpa</span><span class=o>.</span><span class=na>boot</span><span class=o>.</span><span class=na>internal</span><span class=o>.</span><span class=na>EntityManagerFactoryBuilderImpl$JpaEntityNotFoundDelegate</span><span class=o>.</span><span class=na>handleEntityNotFound</span><span class=o>(</span><span class=n>EntityManagerFactoryBuilderImpl</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>163</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>AbstractLazyInitializer</span><span class=o>.</span><span class=na>checkTargetState</span><span class=o>(</span><span class=n>AbstractLazyInitializer</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>286</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>AbstractLazyInitializer</span><span class=o>.</span><span class=na>initialize</span><span class=o>(</span><span class=n>AbstractLazyInitializer</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>181</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>AbstractLazyInitializer</span><span class=o>.</span><span class=na>getImplementation</span><span class=o>(</span><span class=n>AbstractLazyInitializer</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>310</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>pojo</span><span class=o>.</span><span class=na>bytebuddy</span><span class=o>.</span><span class=na>ByteBuddyInterceptor</span><span class=o>.</span><span class=na>intercept</span><span class=o>(</span><span class=n>ByteBuddyInterceptor</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>45</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>hibernate</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>ProxyConfiguration$InterceptorDispatcher</span><span class=o>.</span><span class=na>intercept</span><span class=o>(</span><span class=n>ProxyConfiguration</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>95</span><span class=o>)</span>
</code></pre></td></tr></table></div></div><p>通过使用Hibernate提供的<code>@NotFound(action = NotFoundAction.IGNORE)</code>可以避免这个问题，该注解的默认值是<code>NotFoundAction.EXCEPTION</code>，所以hibernate在join表时查不到对应的数据就会抛出异常。</p><h2 id=连表时指定额外的条件>连表时指定额外的条件</h2><p>连表时使用的<code>@JoinColumn</code>只能指定被连接的表的列，如果需要指定其他的条件，如<code>a inner join b on b.column_1 = 'xxx'</code>，需要使用<code>@JoinColumnsOrFormula</code>注解：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=nd>@Data</span>
<span class=nd>@Entity</span>
<span class=nd>@Table</span><span class=o>(</span><span class=n>name</span> <span class=o>=</span> <span class=s>&#34;navi&#34;</span><span class=o>)</span>
<span class=kd>public</span> <span class=kd>class</span> <span class=nc>Navi</span> <span class=o>{</span>
    <span class=nd>@JsonUnwrapped</span>
    <span class=nd>@OneToOne</span><span class=o>(</span><span class=n>fetch</span> <span class=o>=</span> <span class=n>FetchType</span><span class=o>.</span><span class=na>EAGER</span><span class=o>)</span>
    <span class=nd>@JoinColumnsOrFormulas</span><span class=o>(</span><span class=n>value</span><span class=o>={</span>
            <span class=nd>@JoinColumnOrFormula</span><span class=o>(</span><span class=n>column</span><span class=o>=</span><span class=nd>@JoinColumn</span><span class=o>(</span><span class=n>name</span><span class=o>=</span><span class=s>&#34;label&#34;</span><span class=o>,</span><span class=n>referencedColumnName</span><span class=o>=</span><span class=s>&#34;labelId&#34;</span><span class=o>)),</span>
            <span class=nd>@JoinColumnOrFormula</span><span class=o>(</span><span class=n>formula</span><span class=o>=</span><span class=nd>@JoinFormula</span><span class=o>(</span><span class=n>value</span><span class=o>=</span><span class=s>&#34;&#39;en_US&#39;&#34;</span><span class=o>,</span><span class=n>referencedColumnName</span><span class=o>=</span><span class=s>&#34;locale&#34;</span><span class=o>))</span>
    <span class=o>})</span>
    <span class=kd>private</span> <span class=n>Label</span> <span class=n>label</span><span class=o>;</span>
<span class=o>}</span>

<span class=nd>@Data</span>
<span class=nd>@Entity</span>
<span class=nd>@Table</span><span class=o>(</span><span class=n>name</span><span class=o>=</span><span class=s>&#34;label&#34;</span><span class=o>)</span>
<span class=kd>public</span> <span class=kd>class</span> <span class=nc>Label</span> <span class=o>{</span>
    <span class=kd>private</span> <span class=n>String</span> <span class=n>labelId</span><span class=o>;</span>
    <span class=kd>private</span> <span class=n>String</span> <span class=n>locale</span><span class=o>;</span>
<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>上面的连表sql即为：<code>navi inner join label on navi.label = label.label_id and label.locale = 'en_US'</code>。</p><p>这里的<code>Formula</code>用来定义sql判断，<code>referencedColumnName</code>是被连接的表的字段名字（非数据库列名）。如果嫌麻烦，也可以不写<code>@JoinColumnsOrFormulas</code>，直接用简写的方式：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=nd>@JsonUnwrapped</span>
<span class=nd>@OneToOne</span><span class=o>(</span><span class=n>fetch</span> <span class=o>=</span> <span class=n>FetchType</span><span class=o>.</span><span class=na>EAGER</span><span class=o>)</span>
<span class=nd>@JoinColumnOrFormula</span><span class=o>(</span><span class=n>column</span><span class=o>=</span><span class=nd>@JoinColumn</span><span class=o>(</span><span class=n>name</span><span class=o>=</span><span class=s>&#34;label&#34;</span><span class=o>,</span><span class=n>referencedColumnName</span><span class=o>=</span><span class=s>&#34;labelId&#34;</span><span class=o>)),</span>
<span class=nd>@JoinColumnOrFormula</span><span class=o>(</span><span class=n>formula</span><span class=o>=</span><span class=nd>@JoinFormula</span><span class=o>(</span><span class=n>value</span><span class=o>=</span><span class=s>&#34;&#39;en_US&#39;&#34;</span><span class=o>,</span><span class=n>referencedColumnName</span><span class=o>=</span><span class=s>&#34;locale&#34;</span><span class=o>))</span>
<span class=kd>private</span> <span class=n>Label</span> <span class=n>label</span><span class=o>;</span>
</code></pre></td></tr></table></div></div><p>编译器会自动将多个<code>@JoinColumnOrFormula</code>注解包装成一个<code>@JoinColumnsOrFormulas</code>。</p><h2 id=懒加载导致的n--1问题>懒加载导致的N + 1问题</h2><p>Hibernate的懒加载有个让人诟病的问题，就是所谓的N + 1问题：如果一个实体里存在一个懒加载的集合对象，在查询该实体时，会发出一条SQL。当触发查询该懒加载的集合时，则会发出N条SQL。</p><p>如果这个实体比较复杂，存在多个懒加载的集合，集合对象又各自关联了其他的懒加载的集合，如果触发查询这些集合，就会发出大量的SQL去查询，对DB造成较大的负荷。</p><p>解决方法有如下几种：</p><ol><li>取消懒加载，改为<code>FetchType.EAGER</code>。</li><li>给集合对象添加<code>@Fetch(FetchMode.SUBSELECT)</code>，该注解会让Hibernate只会生成一条SQL去查询该集合。</li><li>使用<code>@NamedEntityGraph</code>和<code>@EntityGraph</code>来解决懒加载时SQL查询过多的问题，但是这种方法比较复杂。</li></ol><ul><li><a href=https://blog.csdn.net/ahilll/article/details/83107982 target=_blank rel="noopener noreffer">解决JPA懒加载典型的N+1问题-注解@NamedEntityGraph</a></li></ul><h2 id=cannot-simultaneously-fetch-multiple-bags异常>cannot simultaneously fetch multiple bags异常</h2><p>应用启动时报错：<code>org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags</code>，该异常由Hibernate引发，当一个实体中定义了两个及两个以上的非懒加载的集合时，即<code>fetch = FetchType.EAGER</code>，这些集合又可能关联其他的对象。Hibernate实现的JPA，默认最高抓取深度含本身级为四级(它有个属性配置是0-3)，若多方(第二级)存在重复值，则第三级中抓取的值就无法映射，就会出现 multiple bags。</p><p>简单来说，Hibernate默认会用一条SQL直接把<code>FetchType.EAGER</code>的集合也一起left join进来，如果这些集合允许重复值，且存在两个及两个以上的这些集合，而集合又可能关联其他的对象。一旦出现这种情况，Hibernate就会无法区分清楚查询回来的结果集。</p><p>解决方法有如下几种：</p><ol><li>改用懒加载<code>FetchType.LAZY</code>来加载这些集合对象。</li><li>给集合对象添加<code>@Fetch(FetchMode.SUBSELECT)</code>，该注解会让Hibernate另外生成一条SQL去查询该集合。效果类似于懒加载，也是用分开的SQL去查询，区别是这个是非懒加载。</li><li>使用Set集合来替代List集合。</li><li>使用<code>@IndexColumn</code>，该注解允许你指明存放索引值的字段，目的跟Set容器不允许重复元素的道理一样。但是该注解以废弃，官方推荐使用的是JPA规范的<code>@OrderColumn</code>。</li></ol><p>前两种方法比较常用，不过第二个方法是Hibernate自身的规范。</p><h2 id=unexpectedrollbackexception异常>UnexpectedRollbackException异常</h2><p>在使用事务时发生异常，事务回滚时报错：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>transaction</span><span class=o>.</span><span class=na>UnexpectedRollbackException</span><span class=o>:</span> <span class=n>Transaction</span> <span class=n>rolled</span> <span class=n>back</span> <span class=n>because</span> <span class=n>it</span> <span class=n>has</span> <span class=n>been</span> <span class=n>marked</span> <span class=n>as</span> <span class=n>rollback</span><span class=o>-</span><span class=n>only</span>
</code></pre></td></tr></table></div></div><p>这是事务传播行为导致的，JPA默认的事务传播级别是<code>PROPAGATION_REQUIRED</code>：如果当前存在事务则加入该事务，否则新建一个事务。</p><p>于是当一个事务方法A去调用了另一个事务方法B时，不指明事务传播级别，那么事务方法B依然使用方法A的事务。此时如果方法B抛出异常，触发事务回滚，而在方法A调用方法B的地方使用try-catch捕获发生的异常，理论上方法A应该继续正常执行，实际上却不是这样。</p><p>当方法A继续执行完毕，在最后提交事务时，会发现当前事务已经被标记为<code>rollback-only</code>状态，于是整个事务回滚并抛出<code>UnexpectedRollbackException</code>异常。</p><p>在这种情况下，一般有两种处理场景：</p><ul><li>只有方法B在遇到异常时事务回滚，且不影响到方法A的事务提交，那么此时方法B的事务要指明为<code>PROPAGATION_NESTED</code>。但是，<strong>JPA默认实现是Hibernate，而Hibernate不提供事务嵌套</strong>。对于这种情况，要么使用其他的JPA实现，要么在方法B中将可能发生的异常try-catch并且不往外抛出，但此时方法B将不能自动事务回滚。</li><li>方法B发生异常时，和方法A一起事务回滚。这种场景需要在方法A调用方法B的地方使用try-catch捕获发生的异常，并且将该异常重新往外抛出，这样就可以让方法A事务回滚，且得到的异常也是真正的异常，而不是UnexpectedRollbackException异常。</li></ul><h2 id=jpa-projection不支持新的日期类localdatelocaldatetime>JPA Projection不支持新的日期类LocalDate、LocalDateTime</h2><p>JPA的Projection有个坑：不支持LocalDate、LocalDateTime这两个类型。代码如下：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=nd>@Query</span><span class=o>(</span><span class=n>value</span> <span class=o>=</span> <span class=s>&#34;select ibs.sequence as sequence, ibs.inspect_booking_id as inspectBookingId, ibs.date as date &#34;</span>
        <span class=o>+</span> <span class=s>&#34;from CNT_INSPECT_BOOKING_SCHEDULED ibs where ibs.inspector_id = :inspectorId&#34;</span><span class=o>,</span> <span class=n>nativeQuery</span> <span class=o>=</span> <span class=kc>true</span><span class=o>)</span>
<span class=n>List</span><span class=o>&lt;</span><span class=n>SimpleInspectBookingScheduled</span><span class=o>&gt;</span> <span class=nf>findInspectBookingIdByInspectorId</span><span class=o>(</span><span class=nd>@Param</span><span class=o>(</span><span class=n>value</span> <span class=o>=</span> <span class=s>&#34;inspectorId&#34;</span><span class=o>)</span><span class=kd>final</span> <span class=n>String</span> <span class=n>inspectorId</span><span class=o>);</span>

<span class=kd>interface</span> <span class=nc>SimpleInspectBookingScheduled</span> <span class=o>{</span>
    <span class=n>Long</span> <span class=nf>getSequence</span><span class=o>();</span>

    <span class=n>String</span> <span class=nf>getInspectBookingId</span><span class=o>();</span>

    <span class=n>LocalDate</span> <span class=nf>getDate</span><span class=o>();</span>
<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>当调用该方法时会抛出如下异常：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=n>java</span><span class=o>.</span><span class=na>lang</span><span class=o>.</span><span class=na>IllegalArgumentException</span><span class=o>:</span> <span class=n>Projection</span> <span class=n>type</span> <span class=n>must</span> <span class=n>be</span> <span class=n>an</span> <span class=n>interface</span><span class=o>!</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>util</span><span class=o>.</span><span class=na>Assert</span><span class=o>.</span><span class=na>isTrue</span><span class=o>(</span><span class=n>Assert</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>121</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>data</span><span class=o>.</span><span class=na>projection</span><span class=o>.</span><span class=na>ProxyProjectionFactory</span><span class=o>.</span><span class=na>createProjection</span><span class=o>(</span><span class=n>ProxyProjectionFactory</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>105</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>data</span><span class=o>.</span><span class=na>projection</span><span class=o>.</span><span class=na>SpelAwareProxyProjectionFactory</span><span class=o>.</span><span class=na>createProjection</span><span class=o>(</span><span class=n>SpelAwareProxyProjectionFactory</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>45</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>data</span><span class=o>.</span><span class=na>projection</span><span class=o>.</span><span class=na>ProjectingMethodInterceptor</span><span class=o>.</span><span class=na>getProjection</span><span class=o>(</span><span class=n>ProjectingMethodInterceptor</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>160</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>data</span><span class=o>.</span><span class=na>projection</span><span class=o>.</span><span class=na>ProjectingMethodInterceptor</span><span class=o>.</span><span class=na>potentiallyConvertResult</span><span class=o>(</span><span class=n>ProjectingMethodInterceptor</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>108</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>data</span><span class=o>.</span><span class=na>projection</span><span class=o>.</span><span class=na>ProjectingMethodInterceptor</span><span class=o>.</span><span class=na>invoke</span><span class=o>(</span><span class=n>ProjectingMethodInterceptor</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>85</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>aop</span><span class=o>.</span><span class=na>framework</span><span class=o>.</span><span class=na>ReflectiveMethodInvocation</span><span class=o>.</span><span class=na>proceed</span><span class=o>(</span><span class=n>ReflectiveMethodInvocation</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>186</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>data</span><span class=o>.</span><span class=na>projection</span><span class=o>.</span><span class=na>ProxyProjectionFactory$TargetAwareMethodInterceptor</span><span class=o>.</span><span class=na>invoke</span><span class=o>(</span><span class=n>ProxyProjectionFactory</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>250</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>aop</span><span class=o>.</span><span class=na>framework</span><span class=o>.</span><span class=na>ReflectiveMethodInvocation</span><span class=o>.</span><span class=na>proceed</span><span class=o>(</span><span class=n>ReflectiveMethodInvocation</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>186</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>data</span><span class=o>.</span><span class=na>projection</span><span class=o>.</span><span class=na>DefaultMethodInvokingMethodInterceptor</span><span class=o>.</span><span class=na>invoke</span><span class=o>(</span><span class=n>DefaultMethodInvokingMethodInterceptor</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>80</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>aop</span><span class=o>.</span><span class=na>framework</span><span class=o>.</span><span class=na>ReflectiveMethodInvocation</span><span class=o>.</span><span class=na>proceed</span><span class=o>(</span><span class=n>ReflectiveMethodInvocation</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>186</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>org</span><span class=o>.</span><span class=na>springframework</span><span class=o>.</span><span class=na>aop</span><span class=o>.</span><span class=na>framework</span><span class=o>.</span><span class=na>JdkDynamicAopProxy</span><span class=o>.</span><span class=na>invoke</span><span class=o>(</span><span class=n>JdkDynamicAopProxy</span><span class=o>.</span><span class=na>java</span><span class=o>:</span><span class=n>215</span><span class=o>)</span>
	<span class=n>at</span> <span class=n>com</span><span class=o>.</span><span class=na>sun</span><span class=o>.</span><span class=na>proxy</span><span class=o>.</span><span class=na>$Proxy611</span><span class=o>.</span><span class=na>getDate</span><span class=o>(</span><span class=n>Unknown</span> <span class=n>Source</span><span class=o>)</span>
</code></pre></td></tr></table></div></div><p>在使用JPA Projection时，对于日期类型必须使用<code>java.sql</code>包下的Date或Timestamp。如果强行使用Java 8新增的日期类，则会抛出上述诡异的异常。将接口改为如下则调用正常：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span><span class=lnt>6
</span><span class=lnt>7
</span><span class=lnt>8
</span><span class=lnt>9
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=kn>import</span> <span class=nn>java.sql.Date</span><span class=o>;</span>

<span class=kd>interface</span> <span class=nc>SimpleInspectBookingScheduled</span> <span class=o>{</span>
    <span class=n>Long</span> <span class=nf>getSequence</span><span class=o>();</span>

    <span class=n>String</span> <span class=nf>getInspectBookingId</span><span class=o>();</span>

    <span class=n>Date</span> <span class=nf>getDate</span><span class=o>();</span>
<span class=o>}</span>
</code></pre></td></tr></table></div></div><p>此外，<code>java.sql</code>包下的类和新的日期类的转换方式可以参考<a href=https://lewky.cn/posts/java-date-issues.html/#javasql%e5%8c%85%e4%b8%8b%e7%9a%84%e7%b1%bb%e5%92%8c%e6%96%b0%e7%9a%84%e6%97%a5%e6%9c%9f%e7%b1%bb%e7%9a%84%e8%bd%ac%e6%8d%a2 target=_blank rel="noopener noreffer">这篇文章</a></p><h2 id=operator-does-not-exist-character-varying--bytea>operator does not exist: character varying = bytea</h2><p>当使用JPA的<code>@Query</code>查询数据库时，此时<code>@Query</code>里自定义的sql会用到参数绑定，如下：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-fallback data-lang=fallback>@Query(value = &#34;SELECT * &#34;
            + &#34;from tb_test test &#34;
            + &#34;where test.domain_id = :domainId&#34;, nativeQuery = true)
List&lt;Test&gt; test(@Param(value = &#34;domainId&#34;) final String domainId);
</code></pre></td></tr></table></div></div><p>如果被绑定的参数值是<code>null</code>，而被查询的数据库是PostgreSQL，那么上述SQL在执行时就会报错：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-fallback data-lang=fallback>Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: character varying = bytea
  Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
  Position: 145
</code></pre></td></tr></table></div></div><p>原因是PostgreSQL驱动把null值识别成了bytea类型，在进行参数绑定时，由于当前字段是varchar类型（character varying），会认为需要进行显示类型转换。如果直接把下述SQL去PostgreSQL 12查询，是不会报错的：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-fallback data-lang=fallback>SELECT * from tb_test test where test.domain_id = null;
</code></pre></td></tr></table></div></div><p>这里报错是因为JPA使用了参数绑定的方式：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-fallback data-lang=fallback>SELECT * from tb_test test where test.domain_id = ?;
</code></pre></td></tr></table></div></div><p>解决这个问题，需要处理参数值是null的情况，由于业务需求，这个参数值不能为null，我需要在参数值不为null时才能调用这个方法，这样就不会触发这个问题。</p><p>如果是需要按照参数值是否为null来作为查询条件，可以这样写：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=nd>@Query</span><span class=o>(</span><span class=n>value</span> <span class=o>=</span> <span class=s>&#34;SELECT * &#34;</span>
            <span class=o>+</span> <span class=s>&#34;from tb_test test &#34;</span>
            <span class=o>+</span> <span class=s>&#34;where test.domain_id is null or test.domain_id = cast(:domainId as text)&#34;</span><span class=o>,</span> <span class=n>nativeQuery</span> <span class=o>=</span> <span class=kc>true</span><span class=o>)</span>
<span class=n>List</span><span class=o>&lt;</span><span class=n>Test</span><span class=o>&gt;</span> <span class=nf>test</span><span class=o>(</span><span class=nd>@Param</span><span class=o>(</span><span class=n>value</span> <span class=o>=</span> <span class=s>&#34;domainId&#34;</span><span class=o>)</span> <span class=kd>final</span> <span class=n>String</span> <span class=n>domainId</span><span class=o>);</span>
</code></pre></td></tr></table></div></div><h2 id=忽略某个字段>忽略某个字段</h2><p>有时候需要在pojo中定义一个常量字段，仅用于业务逻辑，且不希望该字段被映射到数据库中，也就是说这个字段的值不需要被持久化的数据库中。</p><p>这时候可以使用<code>@Transient</code>注解（包路径是<code>javax.persistence.Transient</code>）。</p><h2 id=springboot打印hibernate的sql>SpringBoot打印Hibernate的sql</h2><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span><span class=lnt>6
</span><span class=lnt>7
</span><span class=lnt>8
</span><span class=lnt>9
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-fallback data-lang=fallback># 控制台打印sql语句
spring.jpa.show-sql=true

# 格式化sql语句
spring.jpa.properties.hibernate.format_sql=false

# 指出是什么操作生成了该sql语句
spring.jpa.properties.hibernate.use_sql_comments=false
spring.jpa.properties.hibernate.generate_statistics=false
</code></pre></td></tr></table></div></div><p>如果想把sql也打印的log文件中，logger的配置如下：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span><span class=lnt>5
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-xml data-lang=xml><span class=c>&lt;!-- Use DEBUG level to print sql and sql-parameters, change to INFO level will not print them. --&gt;</span>
<span class=nt>&lt;Logger</span> <span class=na>name=</span><span class=s>&#34;org.hibernate.SQL&#34;</span> <span class=na>level=</span><span class=s>&#34;DEBUG&#34;</span> <span class=na>additivity=</span><span class=s>&#34;true&#34;</span><span class=nt>&gt;</span>
<span class=nt>&lt;/Logger&gt;</span>
<span class=nt>&lt;Logger</span> <span class=na>name=</span><span class=s>&#34;org.hibernate.type.descriptor.sql.BasicBinder&#34;</span> <span class=na>level=</span><span class=s>&#34;INFO&#34;</span> <span class=na>additivity=</span><span class=s>&#34;true&#34;</span><span class=nt>&gt;</span>
<span class=nt>&lt;/Logger&gt;</span>
</code></pre></td></tr></table></div></div><h2 id=detached-entity-passed-to-persist>detached entity passed to persist</h2><p>不要手动设置id的值，如果有其他实体需要用到这个id的值，可以直接getId()来获取id（尽管此时id还没有被hibernate生成出来），hibernate会在commit到db的时候获取到id。</p><p>jpa的Repository的save()有个返回值，返回值是保存之后的对象，虽然此时还没commit到db，但可以通过这个返回值来获取到一些需要提交到db才会生成的数据，如id等。</p><h2 id=id生成策略>id生成策略</h2><p>通常情况下直接用下面的注解来标注一个pojo的id字段即可：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=nd>@Entity</span>
<span class=nd>@Table</span><span class=o>(</span><span class=n>name</span> <span class=o>=</span> <span class=s>&#34;TB_ITEM&#34;</span><span class=o>)</span>
<span class=kd>public</span> <span class=kd>class</span> <span class=nc>Item</span> <span class=o>{</span>

    <span class=nd>@Id</span>
    <span class=nd>@GeneratedValue</span><span class=o>(</span><span class=n>generator</span> <span class=o>=</span> <span class=s>&#34;jpa-uuid&#34;</span><span class=o>)</span>
    <span class=nd>@GenericGenerator</span><span class=o>(</span><span class=n>name</span> <span class=o>=</span> <span class=s>&#34;jpa-uuid&#34;</span><span class=o>,</span> <span class=n>strategy</span> <span class=o>=</span> <span class=s>&#34;uuid&#34;</span><span class=o>)</span>
    <span class=kd>private</span> <span class=n>String</span> <span class=n>id</span><span class=o>;</span>

<span class=o>}</span>
</code></pre></td></tr></table></div></div><p><code>@Id</code>和<code>@GeneratedValue</code>是JPA规范的注解，<code>@GenericGenerator</code>是Hibernate的注解。</p><p><code>@Id</code>指明当前字段是当前pojo的id主键，<code>@GeneratedValue</code>指明使用名为<code>jpa-uuid</code>的id生成器。</p><p><code>@GenericGenerator</code>定义了一个名为<code>@GenericGenerator</code>的id生成器，使用的生成策略是<code>uuid</code>（32位16进制数字）。</p><p>Hibernate除了常见的uuid策略，还提供了其他常见的策略：sequence、identity等。</p><h3 id=sequence策略>sequence策略</h3><p>使用底层数据库的序列机制生成id，换言之，必须用底层数据库支持序列才行。比如MySQL就不支持sequence，但是可以用identity。</p><p>支持序列的有Oracle、PostgreSQL等，使用该策略需要先在数据库创建序列。</p><h3 id=identity策略>identity策略</h3><p>identity同样是由数据库生成的，但该主键字段必须设置为自增长。使用该策略的前提是数据库要支持自动增长类型的字段，Oracle不支持该策略。</p><p>支持自增长的有MySQL、PostgreSQL等，在MySQL中需要将主键设为<code>auto_increment</code>，在PostgreSQL中需要将主键设为<code>serial4</code>或<code>serial8</code>，前者是32位长度，后者是64位长度。</p><h3 id=其他的写法>其他的写法</h3><p>如果不想混用Hibernate的注解，可以用JPA自身提供的生成器注解：<code>@TableGenerator</code>，<code>@SequenceGenerator</code>等，此时需要改变<code>@GeneratedValue</code>的策略。</p><p>下面是样例代码，具体可以参考这篇文章：<a href=https://www.cnblogs.com/frankzone/p/9439143.html target=_blank rel="noopener noreffer">Hibernate学习笔记2.4（Hibernate的Id生成策略）</a></p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt> 1
</span><span class=lnt> 2
</span><span class=lnt> 3
</span><span class=lnt> 4
</span><span class=lnt> 5
</span><span class=lnt> 6
</span><span class=lnt> 7
</span><span class=lnt> 8
</span><span class=lnt> 9
</span><span class=lnt>10
</span><span class=lnt>11
</span><span class=lnt>12
</span><span class=lnt>13
</span><span class=lnt>14
</span><span class=lnt>15
</span><span class=lnt>16
</span><span class=lnt>17
</span><span class=lnt>18
</span><span class=lnt>19
</span><span class=lnt>20
</span><span class=lnt>21
</span><span class=lnt>22
</span><span class=lnt>23
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=c1>// 自增长，适用于支持自增字段的数据库
</span><span class=c1></span><span class=nd>@Id</span>
<span class=nd>@GeneratedValue</span><span class=o>(</span><span class=n>strategy</span> <span class=o>=</span> <span class=n>GenerationType</span><span class=o>.</span><span class=na>IDENTITY</span><span class=o>)</span>

<span class=c1>// 使用表存储生成的主键，可以跨数据库。
</span><span class=c1>// 每次需要主键值时，查询名为&#34;hibernate_table&#34;的表，查找主键列&#34;gen_pk&#34;值为&#34;2&#34;记录，得到这条记录的&#34;gen_val&#34;值，根据这个值，和allocationSize的值生成主键值。
</span><span class=c1></span><span class=nd>@Id</span>
<span class=nd>@GeneratedValue</span><span class=o>(</span><span class=n>strategy</span> <span class=o>=</span> <span class=n>GenerationType</span><span class=o>.</span><span class=na>TABLE</span><span class=o>,</span> <span class=n>generator</span> <span class=o>=</span> <span class=s>&#34;ud&#34;</span><span class=o>)</span>
<span class=nd>@TableGenerator</span><span class=o>(</span><span class=n>name</span> <span class=o>=</span> <span class=s>&#34;ud&#34;</span><span class=o>,</span>
<span class=n>table</span> <span class=o>=</span> <span class=s>&#34;hibernate_table&#34;</span><span class=o>,</span>
<span class=n>pkColumnName</span> <span class=o>=</span> <span class=s>&#34;gen_pk&#34;</span><span class=o>,</span>
<span class=n>pkColumnValue</span> <span class=o>=</span> <span class=s>&#34;2&#34;</span><span class=o>,</span>
<span class=n>valueColumnName</span> <span class=o>=</span> <span class=s>&#34;gen_val&#34;</span><span class=o>,</span>
<span class=n>initialValue</span> <span class=o>=</span> <span class=n>2</span><span class=o>,</span>
<span class=n>allocationSize</span> <span class=o>=</span> <span class=n>5</span><span class=o>)</span>

<span class=c1>// 使用序列
</span><span class=c1></span><span class=nd>@Id</span>
<span class=nd>@GeneratedValue</span><span class=o>(</span><span class=n>strategy</span> <span class=o>=</span> <span class=n>GenerationType</span><span class=o>.</span><span class=na>SEQUENCE</span><span class=o>,</span> <span class=n>generator</span> <span class=o>=</span> <span class=s>&#34;ud&#34;</span><span class=o>)</span>
<span class=nd>@SequenceGenerator</span><span class=o>(</span><span class=n>name</span> <span class=o>=</span> <span class=s>&#34;ud&#34;</span><span class=o>,</span>
<span class=n>sequenceName</span> <span class=o>=</span> <span class=s>&#34;hibernate_seq&#34;</span><span class=o>,</span>
<span class=n>allocationSize</span> <span class=o>=</span> <span class=n>1</span><span class=o>,</span>
<span class=n>initialValue</span> <span class=o>=</span> <span class=n>2</span><span class=o>)</span>
</code></pre></td></tr></table></div></div><h2 id=nativequery>nativeQuery</h2><p>有时候用hql来查询一个复杂的sql会比较麻烦，可以用<code>nativeQuery = true</code>来使用原生sql查询数据：</p><div class=highlight><div class=chroma><table class=lntable><tr><td class=lntd><pre class=chroma><code><span class=lnt>1
</span><span class=lnt>2
</span><span class=lnt>3
</span><span class=lnt>4
</span></code></pre></td><td class=lntd><pre class=chroma><code class=language-java data-lang=java><span class=nd>@Query</span><span class=o>(</span><span class=n>value</span> <span class=o>=</span> <span class=s>&#34;SELECT * &#34;</span>
            <span class=o>+</span> <span class=s>&#34;from tb_test test &#34;</span>
            <span class=o>+</span> <span class=s>&#34;where test.domain_id is null or test.domain_id = cast(:domainId as text)&#34;</span><span class=o>,</span> <span class=n>nativeQuery</span> <span class=o>=</span> <span class=kc>true</span><span class=o>)</span>
<span class=n>List</span><span class=o>&lt;</span><span class=n>Test</span><span class=o>&gt;</span> <span class=nf>test</span><span class=o>(</span><span class=nd>@Param</span><span class=o>(</span><span class=n>value</span> <span class=o>=</span> <span class=s>&#34;domainId&#34;</span><span class=o>)</span> <span class=kd>final</span> <span class=n>String</span> <span class=n>domainId</span><span class=o>);</span>
</code></pre></td></tr></table></div></div><h2 id=参考链接>参考链接</h2><ul><li><a href=https://blog.csdn.net/hsz2568952354/article/details/82724719 target=_blank rel="noopener noreffer">springboot jpa 解决延迟加载问题</a></li><li><a href=https://blog.csdn.net/weixin_43839457/article/details/90445950 target=_blank rel="noopener noreffer">No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor</a></li><li><a href=https://blog.csdn.net/liu_yulong/article/details/84594771 target=_blank rel="noopener noreffer">springboot集成jpa返回Json报错 com.fasterxml.jackson.databind.exc.InvalidDefinitionException:</a></li><li><a href=https://www.cnblogs.com/TTTTT/p/6682798.html target=_blank rel="noopener noreffer">Hibernate和Spring整合出现懒加载异常：org.hibernate.LazyInitializationException: could not initialize proxy - no Session</a></li><li><a href=https://blog.csdn.net/mamingjie12/article/details/25911967 target=_blank rel="noopener noreffer">[JPA] javax.persistence.EntityNotFoundException: Unable to find XXXX with id 0 问题原因</a></li><li><a href=http://blog.sina.com.cn/s/blog_697b968901017w9p.html target=_blank rel="noopener noreffer">[转]cannot simultaneously fetch multiple bags 问题的解决办法</a></li><li><a href="https://segmentfault.com/a/1190000016418596?utm_source=tag-newest" target=_blank rel="noopener noreffer">UnexpectedRollbackException解决方案</a></li><li><a href="https://blog.csdn.net/weixin_33526828/article/details/114507298?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.essearch_pc_relevant&spm=1001.2101.3001.4242" target=_blank rel="noopener noreffer">import java.sql.date_Java8中 LocalDate和java.sql.Date的相互转换操作</a></li><li><a href=https://www.it610.com/article/1289093039972753408.htm target=_blank rel="noopener noreffer">PostgreSQL错误处理“operator does not exist: character varying = bytea at character”</a></li><li><a href=https://blog.csdn.net/Randy_Wang_/article/details/79460306 target=_blank rel="noopener noreffer">Hibernate在控制台打印sql语句以及参数</a></li><li><a href=https://blog.csdn.net/remote_roamer/article/details/5680445 target=_blank rel="noopener noreffer">detached entity passed to persist 错误的引起的原因和解决办法</a></li><li><a href=https://blog.csdn.net/qing_gee/article/details/84655167 target=_blank rel="noopener noreffer">postgresql如何设置自动增长</a></li><li><a href=https://www.cnblogs.com/frankzone/p/9439143.html target=_blank rel="noopener noreffer">Hibernate学习笔记2.4（Hibernate的Id生成策略）</a></li><li><a href=https://stackoverflow.com/questions/39892267/hibernate-onetoone-join-with-additional-criteria target=_blank rel="noopener noreffer">Hibernate oneToOne join with additional criteria</a></li></ul><div class=post-reward><input type=checkbox name=reward id=reward hidden>
<label class=reward-button for=reward>赞赏支持</label><div class=qr-code><label class=qr-code-image for=reward><img class=image src=https://cdn.jsdelivr.net/gh/lewky/lewky.github.io@master/images/common/wechat.png>
<span>微信打赏</span></label>
<label class=qr-code-image for=reward><img class=image src=https://cdn.jsdelivr.net/gh/lewky/lewky.github.io@master/images/common/alipay.png>
<span>支付宝打赏</span></label></div></div></div><div class=post-footer id=post-footer><div class=post-info><div class=post-info-line><div class=post-info-mod><span>更新于 2022-03-01</span></div><div class=post-info-license><span><a rel="license external nofollow noopener noreffer" href=https://creativecommons.org/licenses/by-nc/4.0/ target=_blank>CC BY-NC 4.0</a></span></div></div><div class=post-info-line><div class=post-info-md><span><a class=link-to-markdown href=/posts/spring-data-jpa-hibernate/index.md target=_blank>阅读原始文档</a></span></div><div class=post-info-share><span><a href=javascript:void(0); title="分享到 Twitter" data-sharer=twitter data-url=https://lewky.cn/posts/spring-data-jpa-hibernate/ data-title="Spring Data JPA/Hibernate问题汇总" data-hashtags=JPA,Hibernate,Spring,工作记录><i class="fab fa-twitter fa-fw"></i></a><a href=javascript:void(0); title="分享到 Facebook" data-sharer=facebook data-url=https://lewky.cn/posts/spring-data-jpa-hibernate/ data-hashtag=JPA><i class="fab fa-facebook-square fa-fw"></i></a><a href=javascript:void(0); title="分享到 Hacker News" data-sharer=hackernews data-url=https://lewky.cn/posts/spring-data-jpa-hibernate/ data-title="Spring Data JPA/Hibernate问题汇总"><i class="fab fa-hacker-news fa-fw"></i></a><a href=javascript:void(0); title="分享到 Line" data-sharer=line data-url=https://lewky.cn/posts/spring-data-jpa-hibernate/ data-title="Spring Data JPA/Hibernate问题汇总"><i data-svg-src=https://cdn.jsdelivr.net/npm/simple-icons@2.14.0/icons/line.svg></i></a><a href=javascript:void(0); title="分享到 微博" data-sharer=weibo data-url=https://lewky.cn/posts/spring-data-jpa-hibernate/ data-title="Spring Data JPA/Hibernate问题汇总"><i class="fab fa-weibo fa-fw"></i></a></span></div></div></div><div class=post-info-more><section class=post-tags><i class="fas fa-tags fa-fw"></i>&nbsp;<a href=/tags/jpa/>JPA</a>,&nbsp;<a href=/tags/hibernate/>Hibernate</a>,&nbsp;<a href=/tags/spring/>Spring</a>,&nbsp;<a href=/tags/%E5%B7%A5%E4%BD%9C%E8%AE%B0%E5%BD%95/>工作记录</a></section><section><span><a href=javascript:void(0); onclick=window.history.back();>返回</a></span>&nbsp;|&nbsp;<span><a href=/>主页</a></span></section></div><div class=post-nav><a href=/posts/docsify-0.html/ class=prev rel=prev title=docsify快速入门><i class="fas fa-angle-left fa-fw"></i>docsify快速入门</a>
<a href=/posts/plantuml-class-diagram.html/ class=next rel=next title="PlantUML - 类图">PlantUML - 类图<i class="fas fa-angle-right fa-fw"></i></a></div></div><div id=comments><div id=waline></div><script src=https://cdn.jsdelivr.net/npm/@waline/client/dist/Waline.min.js></script><script>new Waline({el:'#waline',meta:["nick","mail","link"],requiredMeta:["nick","mail"],login:"force",placeholder:"为防恶意灌水攻击，评论前需注册并登录，望见谅~",serverURL:"https://comment.lewky.cn/",avatarCDN:"https://sdn.geekzu.org/avatar/",pageSize:20,avatar:"retro",lang:"zh-CN",visitor:true,highlight:true,uploadImage:false,emoji:['https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/嘉然今天吃什么','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/大航海嘉然','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/向晚大魔王','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/贝拉kira','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/珈乐Carol','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/乃琳Queen','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/EveOneCat','https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/weibo','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/滑稽','https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/emoji/default']});</script></div></article></div></main><footer class=footer><div class=footer-container><div class=footer-line><span id=run-time></span>|
<a href=http://rssblog.vercel.app/ target=_blank rel=noopener title=RSSBlog><i class="fas fa-fw fa-inbox"></i>&nbsp;RSSBlog</a></div><div class=footer-line><i class="far fa-copyright fa-fw"></i><span itemprop=copyrightYear>2018 - 2022</span><span class=author itemprop=copyrightHolder>&nbsp;<a href=/ target=_blank>雨临Lewis</a></span>&nbsp;|&nbsp;<span class=license><a rel="license external nofollow noopener noreffer" href=https://creativecommons.org/licenses/by-nc/4.0/ target=_blank>CC BY-NC 4.0</a></span><span class=icp-splitter>&nbsp;|&nbsp;</span><br class=icp-br><span class=icp><a target=_blank href=http://www.beian.miit.gov.cn/ style=font-weight:700>粤ICP备19103822</a></span></div></div></footer></div><div id=fixed-buttons><a href=# id=back-to-top class=fixed-button title=回到顶部><i class="fas fa-arrow-up fa-fw"></i></a><a href=# id=view-comments class=fixed-button title=查看评论><i class="fas fa-comment fa-fw"></i></a></div><div class=sidebar_wo><div id=leimu><img src=https://cdn.jsdelivr.net/gh/lewky/lewky.github.io@master/images/b2t/leimuA.png alt=雷姆 onmouseover="this.src='https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/b2t/leimuB.png'" onmouseout="this.src='https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/b2t/leimuA.png'" title=回到顶部></div><div class=sidebar_wo id=lamu><img src=https://cdn.jsdelivr.net/gh/lewky/lewky.github.io@master/images/b2t/lamuA.png alt=雷姆 onmouseover="this.src='https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/b2t/lamuB.png'" onmouseout="this.src='https:\/\/cdn.jsdelivr.net\/gh\/lewky\/lewky.github.io@master/images/b2t/lamuA.png'" title=回到底部></div></div><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/lightgallery.js@1.2.0/dist/css/lightgallery.min.css><script type=text/javascript src=https://cdn.jsdelivr.net/npm/smooth-scroll@16.1.3/dist/smooth-scroll.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/lazysizes@5.2.2/lazysizes.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/lightgallery.js@1.2.0/dist/js/lightgallery.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/lg-thumbnail.js@1.2.0/dist/lg-thumbnail.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/lg-zoom.js@1.2.0/dist/lg-zoom.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/sharer.js@0.4.0/sharer.min.js></script><script type=text/javascript>window.config={"code":{"copyTitle":"复制到剪贴板","maxShownLines":30},"comment":{},"lightGallery":{"actualSize":false,"exThumbImage":"data-thumbnail","hideBarsDelay":2000,"selector":".lightgallery","speed":400,"thumbContHeight":80,"thumbWidth":80,"thumbnail":true}};</script><script type=text/javascript src=/js/theme.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/jquery@2.1.3/dist/jquery.min.js></script><script type=text/javascript src=https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js></script><script>var $cdnPrefix="https://cdn.jsdelivr.net/gh/lewky/lewky.github.io@master";</script><script type=text/javascript src=/js/custom.js></script><script>var _hmt=_hmt||[];(function(){var hm=document.createElement("script");hm.src="https://hm.baidu.com/hm.js?6f278ed0fdb01edb3b1e7398379e5432";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm,s);})();</script></body></html>